package thomas.javase.java8;

import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import thomas.javase.java8.entity.People;
import thomas.javase.java8.entity.Student;
import thomas.javase.java8.interfaces.DailyPay;
import thomas.javase.java8.service.PayService;

import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

//@Slf4j
public class Java8Test {

    List<People> peoples = Arrays.asList(new People("Lucy", 20), new People("Lily", 30),
            new People("Andy", 40), new People("William", 50), new People("James", 60));

    @Test
    public void testMap(){
        List<Student> students = peoples.parallelStream().filter(p -> p.getAge() > 30).map(Student::new).collect(Collectors.toList());
        students.forEach(s -> System.out.println(s));
    }
    @Test
    /**
     * You will see this cool pattern. The Stream classes includes a IntStream, LongStream,
     * DoubleStream etc. This is so that you can use primitive types in stream operations.
     * Because otherwise you have to use Stream<Integer> or Stream<Double>, which will box the values.
     *
     * Similarly, the map methods also do this. In the Stream<T> class, there are mapToInt,
     * mapToDouble methods, but the situation is a little bit different in the IntStream, DoubleStream classes.
     *
     * In IntStream, the map method takes an IntUnaryOperator, which maps an int to an int.
     * If you want to map the stream to a Stream<T>, you have to use mapToObj. mapToObj is a good name
     * because it distinguishes from the map that maps to ints. It signifies that the stream changes from a IntStream to a Stream<T>.
     * The reason why mapToObj is named like so is the same reason why mapToInt is named like so - to signify a change in the Stream type/
     */
    public void testMap2(){
        Function<Integer, Integer> times2 = i -> i*2;
        Function<Integer, String> toString = i -> "id: " + i;
        //map and mapToObj seems no difference here
        IntStream.range(0, 20).map(i -> times2.apply(i)).forEach(System.out::println);
        IntStream.range(0, 20).mapToObj(i -> times2.apply(i)).forEach(System.out::println);
        //can't compile
        //IntStream.range(0, 20).map(i -> toString.apply(i)).forEach(System.out::println);
        IntStream.range(0, 20).mapToObj(i -> toString.apply(i)).forEach(System.out::println);
    }


    /**
     * https://blog.csdn.net/qq_39096058/article/details/82864851
     */
    @Test
    public void testFunction(){
        Function<Integer, Integer> times2 = i -> i*2;
        Function<Integer, Integer> squared = i -> i*i;
        System.out.println(times2.apply(4)); //apply is something like run, it is the "calculate" method.
        System.out.println(squared.apply(4));
        //32                先4×4然后16×2,先执行apply(4)，在times2的apply(16),先执行参数，再执行调用者。
        System.out.println(times2.compose(squared).apply(4));
        //64               先4×2,然后8×8,先执行times2的函数，在执行squared的函数。
        System.out.println(times2.andThen(squared).apply(4));
        //16
        System.out.println(Function.identity().compose(squared).apply(4));
    }

    @Test
    public void testIteration(){
        //print 1 -> 9
        IntStream.range(1, 10).forEach(i -> System.out.println(i));
    }

    @Test
    public void testInitialInterface(){
        PayService payService = new PayService((String customer, int amount) -> true);
        payService.dailyPay("Thomas", 10000);
    }
}
